Nachdem wir unseren RandomWalk in den vorherigen Kapiteln sowohl im 2 dimensionalen als auch im 3 dimensionalen Raum umgesetzt haben, erweitern wir das 2 dimensionale Modell jetzt um ein paar neue Bestandteile. Das 2 dimensionale Modell bleibt dabei grundlegend erhalten und wird nur um ein paar Zeilen und ein Modul erweitert, auf die wir im folgenden genauer eingehen und deren Nutzung wir klären.
import random as rd
import numpy
import plotly.graph_objects as go
numPart = 1
pointToHit = numpy.array([50, 50], dtype=int)
NORTH = 1; SOUTH = 2; WEST = 3; EAST = 4
Wie auch beim normalen 2 dimensionalen Modell gibt es wieder die 4 Richtungen in die wir uns bewegen können und die wir nutzen um unseren Schritt zu erstellen. Neu ist hier die Variable pointToHit, die durch ein Numpy-Array repräsentiert wird und Werte vom Typ int enthält. Sie enthält den Punkt, den wir suchen und den wir im nachfolgenden Modul nutzen um zu prüfen ob wir diesen Punkt gefunden haben.
def hitCheck(pointToHit, realPoint):
return numpy.array_equal(pointToHit, realPoint)
Das Modul hitCheck ist ebenfall neu, und bekommt 2 Numpy-Arrays übergeben. Diese Arrays vergleichen wir mit Hilfe der numpy-Methode array_equal, genauer gesagt vergleichen wir sie auf Gleichheit der Form der beiden Arrays und auf Gleicheheit der Elemente. Nur wenn beide Werte übereinstimmen wird True zurückgegeben, ansonsten übergeben wir False. Diesen zurückgegebenen Wert greifen wir später in einem anderne Modul wieder auf.
def singleStep():
position = numpy.zeros(shape=(2,), dtype=int)
currentStep = rd.randint(1, 4 + 1)
position[1] += numpy.where(currentStep == NORTH, 1, 0)
position[1] -= numpy.where(currentStep == SOUTH, 1, 0)
position[0] += numpy.where(currentStep == EAST, 1, 0)
position[0] -= numpy.where(currentStep == WEST, 1, 0)
return position
Die Methode singleStep bleibt im Verlgeich zum normalen 2 dimensionalen RandomWalk unverändert und kann einfach übernommen werden. Ihr Ergbenis nutzen wir in der walkTillHit-Methode um die Positionen zu erzeugen.
def walkTillHit(numPart=1, maxStep=5):
positionsArray = numpy.zeros(shape=(maxStep+1, 2), dtype=int)
for step in numpy.arange(maxStep):
positionsArray[step+1, ] = positionsArray[step, ] + singleStep()
if hitCheck(pointToHit, positionsArray[step+1, ]):
tuple = (positionsArray, step+1, maxStep)
return tuple
break
tuple = (positionsArray, step+1, maxStep)
return tuple
Der walkTillHit-Methode übergeben wir auch hier numPart und maxStep wobei maxStep hier numStep entspricht. Auch hier beginnt das positionsArray beim Punkt (0,0) und fasst Daten vom Typ int. Für die Anzahl der Schritte erzeugen wir auch hier in einer Schleife wieder neue Schritte, indem wir die aktuelle Position mit Hilfe des Schritts und der letzten Position berechnen. Allerdings beginnt die Schleife hier nicht direkt von neuem, sondern wir rufen zuerst die hitCheck-Methode auf. Dieser übergeben wir den Punkt der getroffen werden soll und die gerade errechnete Position. Als Ergebnis erhalten wir entweder True oder False, was wir in einem if-Statement abfragen. springen wir in den if-Fall, so bilden wir ein Tuple aus dem positionsArray, dem int-Wert step+1 und der Variable maxStep, returnen das Tuple und verlassen die Schleife mit einem break. Da der else-Fall nicht genauer definiert ist, läuft die Schleife hier noch einmal durch und prüft erneut den Punkt. Das Programm kann also entweder vorzeitig beendet werden wenn wir den gesuchten Punkt erreichen, oder es läuft so lange durch, bis wir alle durch maxStep übergebenen Schritte aufgebraucht haben, mit dem Ergebnis, dass wir den gesuchten Punkt nicht finden. Läuft die Schleife alle Schritte ab und der Punkt wird nicht gefunden, bilden wir das gleiche Tuple wie im if-Fall und returnen es.
def draw(tuple):
print(tuple[0], pointToHit.shape)
posArray = tuple[0]
fig = go.Figure(data=go.Scatter(
x=posArray[:tuple[1], 0],
y=posArray[:tuple[1], 1],
mode='markers',
name='Walk till point is hit',
marker=dict(
color=numpy.arange(tuple[2]),
size=5,
colorscale='Reds',
showscale=True)
))
fig.add_trace(
go.Scatter(
x=posArray[tuple[1]:, 0],
y=posArray[tuple[1]:, 1],
mode='markers',
marker_color='Green',
hovertext=('Last point')))
fig.show()
In der draw-Methode erwarten wir das Tuple als Eingangsparameter. Wir schreiben uns das an Index 0 stehende positionsArray wieder in eine eigene Variable, um die Lesbarkeit zu verbessern. Der restliche Code zum zeichnen der Grafik ist wieder gleich mit dem 2 dimensionalen RandomWalk. Mit Hilfe von fig.add_trace weisen wir der Grafik den letzten Punkz des Arrays als grünen Marker zu um diesen besser kenntlich zu machen.
import random as rd
import numpy
import plotly.graph_objects as go
numPart = 1
pointToHit = numpy.array([50, 50], dtype=int)
NORTH = 1; SOUTH = 2; WEST = 3; EAST = 4
def hitCheck(pointToHit, realPoint):
return numpy.array_equal(pointToHit, realPoint)
def singleStep():
position = numpy.zeros(shape=(2,), dtype=int)
currentStep = rd.randint(1, 4 + 1)
position[1] += numpy.where(currentStep == NORTH, 1, 0)
position[1] -= numpy.where(currentStep == SOUTH, 1, 0)
position[0] += numpy.where(currentStep == EAST, 1, 0)
position[0] -= numpy.where(currentStep == WEST, 1, 0)
return position
def walkTillHit(numPart=1, maxStep=5):
# positionsArray wird direkt auf 0,0 als Startpunkt gesetzt
positionsArray = numpy.zeros(shape=(maxStep+1, 2), dtype=int)
for step in numpy.arange(maxStep):
positionsArray[step+1, ] = positionsArray[step, ] + singleStep()
if hitCheck(pointToHit, positionsArray[step+1, ]):
tuple = (positionsArray, step+1, maxStep)
return tuple
break
tuple = (positionsArray, step+1, maxStep)
return tuple
def draw(tuple):
posArray = tuple[0]
fig = go.Figure(data=go.Scatter(
x=posArray[:tuple[1], 0],
y=posArray[:tuple[1], 1],
mode='markers',
name='Walk till point is hit',
marker=dict(
color=numpy.arange(tuple[2]),
size=5,
colorscale='Reds',
showscale=True)
))
fig.add_trace(
go.Scatter(
x=posArray[tuple[1]:, 0],
y=posArray[tuple[1]:, 1],
mode='markers',
marker_color='Green',
hovertext=('Last point')))
fig.update_layout(
autosize=False,
height=800,
width=1310)
fig.show()
def main():
draw(walkTillHit(1, 5000))
if __name__ == "__main__":
main()
Führen wir das Programm mehrmals hintereinander aus, so werden einige Dinge schnell deutlich: zum einen ist es auf keinen Fall garantiert, dass wir den gewählten Punkt in jedem Durchlauf treffen, zum anderen verändert sich das entstehende Muster mit jedem Durchlauf. Außerdem ist der tatsächliche Zufall der Bewegungen vor allem bei großen Datenmengen gut erkennbar.
Im folgenden Kapitel wollen wir mit Hilfe des hier erstellten Modells eine große Menge Zufallszahlen generieren und später in einer Heatmap darstellen.